package com.googlecode.project4uz.decoder;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import com.googlecode.project4uz.interfaces.Loggable;

/**
 * Klasa zapisuje publiczne pola do obiektow.
 * Dla uzyskania wiekszej ilosci informacji,
 * polecam przejrzec dokument <i>Mechanizm_refleksji</i> na wiki.
 * 
 * @author Piotr Broniszewski
 * @author Artur Gertych
 * @author Daniel Kur
 * @author Tomasz Gidlewicz
 * 
 * @see http://code.google.com/p/project4uz/wiki/Mechanizm_refleksji
 */
public class ObjectWriter implements Loggable
{
	/**
	 * Biezacy obiekt referencyjny w kluczu w deserializowanej mapie.
	 */
	private Object keyObject;
	
	/**
	 * Flaga ustawiana w przypadku, gdy klucz w mapie to obiekt referencyjny.
	 */
	private boolean keyIsReference;
	
	/**
	 * Flaga ustawiana w przypadku, gdy wartosc w mapie to obiekt referencyjny.
	 */
	private boolean valueIsReference;
	
	/**
	 * Biezaca wartosc klucza w deserializowanej mapie.
	 */
	private String keyValue;
	
	/**
	 * Nazwa metody do parsowania dla biezacej wartosci klucza.
	 */
	private String keyMethod;
	
	/**
	 * Metoda do parsowania biezacej wartosci klucza.
	 */
	private Method keyParse;
	
	/**
	 * Reprezentacja referencji do deserializowanych obiektow.
	 */
	private Map<String, Object> references;
	
	/**
	 * Deserializowane obiekty.
	 */
	private LinkedList<String> objects;
	
	/**
	 * Mapa typow prymitywnych.
	 */
	private static final Map<String, Class<?>> PRIMITIVENAME = new HashMap<String, Class<?>>(8);	
	static
	{
		PRIMITIVENAME.put("boolean", boolean.class);
		PRIMITIVENAME.put("byte", byte.class);
		PRIMITIVENAME.put("char", char.class);
		PRIMITIVENAME.put("double", double.class);
		PRIMITIVENAME.put("float", float.class);
		PRIMITIVENAME.put("int", int.class);
		PRIMITIVENAME.put("long", long.class);
		PRIMITIVENAME.put("short", short.class);
	}
	
	/**
	 * Tworzy obiekt zapisujacy publiczne pola do innych obiektow.
	 */
	public ObjectWriter()
	{
		this.references = new HashMap<String, Object>();
		this.objects = new LinkedList<String>();
	}
	
	/**
	 * Pobiera juz zdeserializowany obiekt.
	 */
	public Object readObject()
	{
		return references.get(objects.remove());
	}
	
	/**
	 * Pobiera klase na podstawie jej tekstowej reprezentacji.
	 * @param className - nazwa klasy w postaci tekstu.
	 * @return klasa utworzona na podstawie Stringa <i>className</i>.
	 */
	final static Class<?> getClassFromName(String className)
	{		
		Class<?> cls = PRIMITIVENAME.get(className);
		if (cls != null)
			return cls;
		else if (className.endsWith("[]"))
			return Array.newInstance(getClassFromName(className.substring(0, className.length()-2)), 0).getClass();
		else
			try
			{
				return ClassLoader.getSystemClassLoader().loadClass(className);
			}
			catch (ClassNotFoundException e)
			{
				LOG.warning("Nie moge wczytac klasy: " + className);
				return null;
			}
	}
	
	/**
	 * Pobiera nazwe klasy z URI w formacie RDF.
	 * @param objectURI - identyfikator obiektu odczytany z pliku RDF.
	 * @return nazwa klasy.
	 */
	final static String parseClassName(final String objectURI)
	{
		return parseClassName(objectURI, '#');
	}
	
	/**
	 * Pobiera nazwe klasy z URI w formacie RDF.
	 * @param objectURI - identyfikator obiektu odczytany z pliku RDF;
	 * @param separator - separator odzielajacy nazwe klasy w pliku RDF.
	 * @return nazwa klasy.
	 */
	final static String parseClassName(final String objectURI, final char separator)
	{
		int i = objectURI.length() - 3;
		while (objectURI.charAt(i) != separator) i--;
		return objectURI.substring(5, i);
	}
	
	/**
	 * Pobiera nazwe tablicy z URI w formacie RDF.
	 * @param objectURI - identyfikator tablicy odczytany z pliku RDF.
	 * @return nazwa tablicy.
	 */
	final static String parseArrayComponent(final String objectURI)
	{
		StringBuilder arrayName = new StringBuilder(parseClassName(objectURI));
		int i = objectURI.length() - 3;
		int j = objectURI.length() - 4;
		while (objectURI.charAt(i) != '/') i--;
		while (objectURI.charAt(j) != 'D') j--;
		
		int dimension = Integer.parseInt(objectURI.substring(++j, i))-1;
		while (dimension > 0)
		{
			arrayName.append("[]");
			--dimension;
		}
		return arrayName.toString();
	}
	
	/**
	 * Tworzy instancje podanego obiektu klasy.
	 * @param object - nazwa obiektu.
	 */
	final void setObject(String object)
	{
		try
		{
			references.put(object, getClassFromName(parseClassName(object, '#')).newInstance());
			objects.add(object);
		}
		catch (InstantiationException e)
		{
			try
			{
				Constructor<?> cr = Class.forName(parseClassName(object, '#')).getDeclaredConstructor(new Class[]{Class.forName(parseClassName (object, '$'))});
				cr.setAccessible (true);
				references.put(object, cr.newInstance(new Object[]{null}));
				objects.add(object);
			}
			catch (InstantiationException e1)
			{
				LOG.warning("Nie moge zainicjalizowac obiektu: " + object);
			}
			catch (Exception e1)
			{
				LOG.warning("Nie moge utworzyc obiektu: " + object);
			}
		}
		catch (Exception e)
		{
			LOG.warning("Nie moge utworzyc obiektu: " + object);
		}
	}
	
	/**
	 * Sprawdza, czy obiekt moze byc deserializowany.
	 * @param object - nazwa obiektu;
	 * @param value - wartosc.
	 * @return <i>true</i>, jezeli obiekt moze byc deserializowany
	 * lub <i>false</i>, jezeli obiekt nie moze byc deserializowany.
	 */
	final boolean isSerializable(String object, String value)
	{		
		try
		{
			Object o = references.get(object);
			Field field = o.getClass().getDeclaredField("serialVersionUID");
			field.setAccessible(true);
			if (field.getLong(o) == Long.parseLong(value))
				return true;
		}
		catch (Exception e)
		{
			LOG.warning("Nie moge deserializowac obiektu:" + object);
		}
		return false;
	}
	
	/**
	 * Inicjalizuje zmienna primitywna.
	 * @param object - ID obiektu;
	 * @param name - nazwa prymitywu;
	 * @param value - wartosc prymitywu.
	 * @throws ArrayIndexOutOfBoundsException
	 * @throws NumberFormatException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws NoSuchMethodException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setPrimitive(String object, String name, String value) throws ArrayIndexOutOfBoundsException, NumberFormatException, SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
	{		
		try
		{
			Object o = references.get(object);
			Field field;
			try
			{
				field = o.getClass().getField(name);
			}
			catch(Exception e)
			{
				field = o.getClass().getDeclaredField(name);
				field.setAccessible(true);
			}
			
			String method = "parse" + field.get(o).getClass().getSimpleName();
			Method parse = PrimitiveParser.class.getDeclaredMethod(method, String.class);
			parse.setAccessible(true);
			field.set(o, parse.invoke(null, value));
		}
		catch (NoSuchFieldException e)
		{

			this.setPrimitiveInArray(object, Integer.parseInt(name.substring(1)), value);	
		}
		catch (Exception e)
		{
			LOG.warning("Nie moge zainicjalizowac prymitywu: " + object + "." + name);
		}
	}
	
	/**
	 * Inicjalizuje zmienna referencyjna.
	 * @param object - ID obiektu;
	 * @param name - nazwa referencji;
	 * @param reference - wartosc referencji.
	 * @throws NumberFormatException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 * @throws NoSuchMethodException
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 */
	final void setReference(String object, String name, String reference) throws NumberFormatException, SecurityException, IllegalArgumentException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException
	{
		try
		{
			Object o = references.get(object);			
			Field field;
			try
			{
				field = o.getClass().getField(name);
			}
			catch(Exception e1)
			{
				field = o.getClass().getDeclaredField(name);
				field.setAccessible(true);
			}
			references.put(reference, getClassFromName(parseClassName(reference, '#')).newInstance());			
			field.set(o, references.get (reference));
		}
		catch (InstantiationException e)
		{
			try
			{
				Object o = references.get(object);
				Field field;
				try
				{
					field = o.getClass().getField(name);
				}
				catch(Exception e1)
				{
					field = o.getClass().getDeclaredField(name);
					field.setAccessible(true);
				}
				Constructor<?> cr = Class.forName(parseClassName(reference, '#')).getDeclaredConstructor(new Class[]{Class.forName(parseClassName(reference, '$'))});
				cr.setAccessible(true);
				references.put(reference, cr.newInstance(new Object[]{null}));
				field.set(o, references.get(reference));
			}
			catch (InstantiationException e1)
			{
				LOG.warning("Nie moge zainicjalizowac referencji: " + object + "." + name);
			}
			catch (Exception e1)
			{
				LOG.warning("Nie moge utworzyc referencji: " + object + "." + name);
			}
		}
		catch (NoSuchFieldException e)
		{
			this.setReferenceInArray(object, Integer.parseInt (name.substring (1)), reference, name);
		}
		catch (Exception e)
		{
			LOG.warning("Nie moge utworzyc referencji: " + object + "." + name);
		}
	}
	
	/**
	 * Inicjalizuje tablice zmiennych.
	 * @param object - ID obiektu;
	 * @param name - nazwa tablicy;
	 * @param arrayID - ID tablicy;
	 * @param length - dlugosc tablicy.
	 * @throws NumberFormatException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setArray(String object, String name, String arrayID, int length) throws NumberFormatException, SecurityException, IllegalArgumentException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException
	{
		try
		{
			Object o = references.get(object);
			Field field;
			try
			{
				field = o.getClass().getField(name);
			}
			catch(Exception e1)
			{
				field = o.getClass().getDeclaredField(name);
				field.setAccessible(true);
			}
			references.put(arrayID, Array.newInstance(getClassFromName(parseArrayComponent(arrayID)), length));
			field.set(o, references.get(arrayID));
		}
		catch (NoSuchFieldException e1)
		{
			this.setArrayInArray(object, Integer.parseInt(name.substring(1)), arrayID, name);
		}
		catch (Exception e)
		{
			try
			{
				Object o = references.get(object);
				Field field;
				try
				{
					field = o.getClass().getField(name);
				}
				catch(Exception e1)
				{
					field = o.getClass().getDeclaredField(name);
					field.setAccessible(true);
				}
				references.put(arrayID, Array.newInstance(getClassFromName(parseArrayComponent(arrayID)), length));
				LOG.warning(references.get(arrayID).toString());
				field.set(o, references.get(arrayID));
			}
			catch (NoSuchFieldException e1)
			{
				this.setArrayInArray(object, Integer.parseInt(name.substring(1)), arrayID, name);
			}
		}
	}
	
	/**
	 * Inicjalizuje kolekcje.
	 * @param object - ID obiektu;
	 * @param collectionID - ID kolekcji;
	 * @param name - nazwa kolekcji;
	 * @param type - typ kolekcji.
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws IllegalArgumentException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setCollection(String object, String collectionID, String name, String type) throws SecurityException, NoSuchFieldException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
	{
		Object o = references.get(object);
		Field field;
		try
		{
			field = o.getClass().getField(name);
		}
		catch(Exception e1)
		{
			field = o.getClass().getDeclaredField(name);
			field.setAccessible(true);
		}
		Constructor<?> cr = Class.forName(type).getDeclaredConstructor();
		cr.setAccessible(true);
		
		Collection<?> col = (Collection<?>) cr.newInstance(new Object[]{});
		references.put(collectionID, col);
		System.out.println(">>  " + collectionID.toString());
		field.set(o, references.get(collectionID));
	}
	
	/**
	 * Inicjalizuje kolekcje zmiennymi.
	 * @param object - ID obiektu;
	 * @param value - wartosc prymitywu.
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setPrimitiveInCollection(String object, String value) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
	{
		Object o = references.get(object);
		
		char[] typeName = RioReader.parseCollectionFieldTypeShort(object, true).toCharArray();
		typeName[0] = Character.toUpperCase(typeName[0]);
		String method = "parse" + String.copyValueOf(typeName);
		Method parse = PrimitiveParser.class.getDeclaredMethod(method, String.class);
		parse.setAccessible(true);

		@SuppressWarnings("unchecked")
		Collection<Object> col = (Collection<Object>)o;
		col.add(parse.invoke(method, value));
	}
	
	/**
	 * Inicjalizuje kolekcje referancjami.
	 * @param object - ID obiektu;
	 * @param reference - wartosc referencji;
	 * @param name - nazwa kolekcji.
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws IllegalArgumentException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setReferenceInCollection(String object, String reference, String name) throws SecurityException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
	{
		try
		{
			Object o = references.get(object);
			references.put(reference, Class.forName(parseClassName(reference, '#')).newInstance());
			
			Object oRef = references.get(reference);
			
			@SuppressWarnings("unchecked")
			Collection<Object> col = (Collection<Object>)o;
			col.add(oRef);
			System.out.println(">>>>>>> : <<<<<<< " + o.toString());
		}
		catch (InstantiationException e)
		{
			Object o = references.get(object);
			Constructor<?> cr = Class.forName(parseClassName(reference, '#')).getDeclaredConstructor(new Class[]{Class.forName(parseClassName(reference, '$'))});
			cr.setAccessible(true);
			references.put(reference, cr.newInstance(new Object[]{null}));
			Object oRef = references.get(reference);
			@SuppressWarnings("unchecked")
			Collection<Object> col = (Collection<Object>)o;
			col.add(oRef);
			System.out.println(">>>>>>> : <<<<<<< " + o.toString());
		}
		catch (IllegalArgumentException e)
		{
			LOG.warning("Nie moge przypisac referencji.");
		}
	}
	
	/**
	 * Inicjalizuje mape.
	 * @param object - ID obiektu;
	 * @param mapID - ID mapy;
	 * @param name - nazwa mapy;
	 * @param type - typ mapy.
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws IllegalArgumentException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setMap(String object, String mapID, String name, String type) throws SecurityException, NoSuchFieldException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
	{
		Object o = references.get(object);
		Field field;
		try
		{
			field = o.getClass().getField(name);
		}
		catch(Exception e1)
		{
			field = o.getClass().getDeclaredField(name);
			field.setAccessible(true);
		}
		Constructor<?> cr = Class.forName(type).getDeclaredConstructor();
		cr.setAccessible(true);
		
		Map<?, ?> map = (Map<?, ?>) cr.newInstance(new Object[]{});
		references.put(mapID, map);
		field.set(o, references.get(mapID));
	}
	
	/**
	 * Inicjalizuje mape zmiennymi.
	 * @param object - ID obiektu;
	 * @param keyValue - klucz lub wartosc;
	 * @param keyOrValue - rozpoznanie deserializowanej zmiennej (klucz czy wartosc).
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws InstantiationException 
	 * @throws NoSuchMethodException 
	 * @throws ClassNotFoundException 
	 */
	final void setPrimitiveInMap(String object, String keyValue, String keyOrValue, String name) throws SecurityException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException, InstantiationException
	{
		Object o = references.get(object);
		
		if (keyOrValue.equals("key:/"))
		{
			try
			{
				this.keyValue = keyValue;
				char[] typeName = RioReader.parseCollectionFieldTypeShort(object, false).toCharArray();
				typeName[0] = Character.toUpperCase(typeName[0]);
				keyMethod = "parse" + String.copyValueOf(typeName);
				keyParse = PrimitiveParser.class.getDeclaredMethod(keyMethod, String.class);
				keyParse.setAccessible(true);
			}
			catch (NoSuchMethodException e)
			{
				keyIsReference = true;
				setReferenceInMap(object, keyValue, keyOrValue, name);
			}
		}
		
		else if (keyOrValue.equals("value:/"))
		{
			try
			{
				char[] typeName = RioReader.parseCollectionFieldTypeShort(object, true).toCharArray();
				typeName[0] = Character.toUpperCase(typeName[0]);
				String method = "parse" + String.copyValueOf(typeName);
				Method parse = PrimitiveParser.class.getDeclaredMethod(method, String.class);
				parse.setAccessible(true);

				Map<Object, Object> map = (Map<Object, Object>)o;
				if (keyIsReference)
				{
					map.put(keyObject, parse.invoke(method, keyValue));
					keyIsReference = false;
				}
				else map.put(keyParse.invoke(keyMethod, this.keyValue), parse.invoke(method, keyValue));

				System.out.println(">>>>>>:<<<<<< " + o.toString());
			}
			catch (NoSuchMethodException e)
			{
				valueIsReference = true;
				setReferenceInMap(object, keyValue, keyOrValue, name);
			}
		}
	}
	
	final void setReferenceInMap(String object, String reference, String keyOrValue, String name) throws IllegalAccessException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, InvocationTargetException
	{
		try
		{
			Object o = references.get(object);
			references.put(reference, Class.forName(parseClassName(reference, '#')).newInstance());
			
			if (keyOrValue.equals("key:/"))
			{
				keyObject = references.get(reference);
			}
			
			else if (keyOrValue.equals("value:/"))
			{
				Object oRef = references.get(reference);
				Map<Object, Object> map = (Map<Object, Object>)o;
				
				if (keyIsReference && valueIsReference)
				{
					map.put(keyObject, oRef);
					keyIsReference = false;
					valueIsReference = false;
				}
				else if (!keyIsReference && valueIsReference)
				{
					map.put(keyParse.invoke(keyMethod, this.keyValue), oRef);
					valueIsReference = false;
				}
				
				System.out.println(">>>>>>> : <<<<<<< " + o.toString());
			}
		}
		catch (InstantiationException e)
		{
			Object o = references.get(object);
			Constructor<?> cr = Class.forName(parseClassName(reference, '#')).getDeclaredConstructor(new Class[]{Class.forName(parseClassName(reference, '$'))});
			cr.setAccessible(true);
			references.put(reference, cr.newInstance(new Object[]{null}));
			
			if (keyOrValue.equals("key:/"))
			{
				keyObject = references.get(reference);
			}
			
			else if (keyOrValue.equals("value:/"))
			{
				Object oRef = references.get(reference);
				Map<Object, Object> map = (Map<Object, Object>)o;
				
				if (keyIsReference && valueIsReference)
				{
					map.put(keyObject, oRef);
					keyIsReference = false;
					valueIsReference = false;
				}
				else if (!keyIsReference && valueIsReference)
				{
					map.put(keyParse.invoke(keyMethod, this.keyValue), oRef);
					valueIsReference = false;
				}
				
				System.out.println(">>>>>>> : <<<<<<< " + o.toString());
			}
		}
		catch (IllegalArgumentException e)
		{
			LOG.warning("Nie moge przypisac referencji.");
		}
	}
	
	/**
	 * Inicjalizuje obiekt tablicy zmienna prymitywna.
	 * @param object - ID obiektu;
	 * @param index - biezacy index tablicy;
	 * @param value - wartosc prymitywu;
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws ArrayIndexOutOfBoundsException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setPrimitiveInArray(String object, int index, String value) throws SecurityException, NoSuchMethodException, ArrayIndexOutOfBoundsException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
	{
		Object o = references.get(object);
		char[] typeName = o.getClass().getComponentType().getSimpleName().toCharArray();
		typeName[0] = Character.toUpperCase (typeName[0]);
		String method = "parse" + String.copyValueOf(typeName);
		Method parse = PrimitiveParser.class.getDeclaredMethod(method, String.class);
		parse.setAccessible(true);
		Array.set(o, index, parse.invoke(null, value));
	}
	
	/**
	 * Inicjalizuje obiekt tablicy zmienna referencyjna.
	 * @param object - ID obiektu;
	 * @param index - biezacy indeks tablicy;
	 * @param reference - wartosc referencji;
	 * @param name - nazwa tablicy.
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 */
	final void setReferenceInArray(String object, int index, String reference, String name) throws IllegalAccessException, ClassNotFoundException, SecurityException, NoSuchMethodException, InstantiationException, InvocationTargetException
	{
		try
		{
			Object o = references.get(object);
			references.put(reference, Class.forName(parseClassName(reference, '#')).newInstance());
			Array.set(o, index, references.get(reference));
		}
		catch (InstantiationException e)
		{
			Object o = references.get(object);
			Constructor<?> cr = Class.forName(parseClassName(reference, '#')).getDeclaredConstructor(new Class[]{Class.forName(parseClassName(reference, '$'))});
			cr.setAccessible(true);
			references.put(reference, cr.newInstance(new Object[]{null}));
			Array.set(o, index, references.get(reference));
		}
		catch (IllegalArgumentException e)
		{
			LOG.warning("Nie moge przypisac referencji.");
		}
	}
	
	/**
	 * Inicjalizuje obiekt tablicy zmienna tablicowa.
	 * @param object - ID obiektu;
	 * @param index - biezacy indeks tablicy;
	 * @param reference - wartosc referencji;
	 * @param name - nazwa tablicy.
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws IllegalArgumentException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	final void setArrayInArray(String object, int index, String reference, String name) throws SecurityException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
	{
		try
		{
			Object o = references.get(object);
			references.put(reference, Array.newInstance(getClassFromName(parseArrayComponent(reference)), RioReader.parseArraySize(reference)));
			Array.set(o, index, references.get(reference));
		}
		catch (Exception e)
		{
			LOG.warning("Exception: setArrayInArray");
			
			Object o = references.get(object);
			Constructor<?> cr = Class.forName(parseClassName(reference, '#')).getDeclaredConstructor(new Class[]{Class.forName(parseClassName(reference, '$'))});
			cr.setAccessible(true);
			references.put(reference, cr.newInstance(new Object[]{null}));
			Array.set(o, index, references.get(reference));
		}		
	}
}
